/**
 * @Author: aesoper
 * @Description:
 * @File:  ssdb
 * @Version: 1.0.0
 * @Date: 2020/6/4 19:14
 */

package ssdb

import (
	"errors"
	"gitee.com/aesoper/cache/factory"
	"github.com/seefan/gossdb"
	"github.com/seefan/gossdb/conf"
	"math"
	"sync"
	"time"
)

type (
	Cache struct {
		cfg  Options
		conn *gossdb.Connectors
		mux  sync.RWMutex
	}

	Options struct {
		Host     string `mapstructure:"host"`
		Port     int    `mapstructure:"port"`
		Password string `mapstructure:"password"`
	}
)

func (o Options) IsValid() error {
	if o.Port > 65535 || o.Port <= 0 {
		return errors.New("port invalid")
	}
	return nil
}

func (c *Cache) Get(key string) ([]byte, error) {
	client, err := c.conn.NewClient()
	if err != nil {
		return nil, err
	}

	c.mux.RLock()
	defer c.mux.RUnlock()

	val, err := client.Get(key)
	if err != nil {
		return nil, err
	}
	return val.Bytes(), nil
}

func (c *Cache) Put(key string, val interface{}, timeout time.Duration) error {
	client, err := c.conn.NewClient()
	if err != nil {
		return err
	}

	c.mux.Lock()
	defer c.mux.Unlock()
	return client.Set(key, val, int64(timeout.Seconds()))
}

func (c *Cache) Delete(key string) error {
	client, err := c.conn.NewClient()
	if err != nil {
		return err
	}
	c.mux.Lock()
	defer c.mux.Unlock()
	return client.Del(key)
}

func (c *Cache) Incr(key string) error {
	client, err := c.conn.NewClient()
	if err != nil {
		return err
	}
	c.mux.Lock()
	defer c.mux.Unlock()
	_, err = client.Incr(key, 1)
	return err
}

func (c *Cache) Decr(key string) error {
	client, err := c.conn.NewClient()
	if err != nil {
		return err
	}
	c.mux.Lock()
	defer c.mux.Unlock()
	_, err = client.Incr(key, -1)
	return err
}

func (c *Cache) IsExist(key string) bool {
	client, err := c.conn.NewClient()
	if err != nil {
		return false
	}
	c.mux.RLock()
	defer c.mux.RUnlock()
	exists, _ := client.Exists(key)
	return exists
}

func (c *Cache) ClearAll() error {
	client, err := c.conn.NewClient()
	if err != nil {
		return err
	}
	c.mux.Lock()
	defer c.mux.Unlock()
	_, err = client.Keys("", "", int64(math.Pow10(10)))
	return err
}

func (c *Cache) StartAndConfigure(ops ...factory.CacheOption) error {
	for _, op := range ops {
		op(c)
	}
	if err := c.initialize(); err != nil {
		return err
	}

	return nil
}

func (c *Cache) initialize() error {

	conn, err := gossdb.NewPool(&conf.Config{
		Host:             c.cfg.Host,
		Port:             c.cfg.Port,
		GetClientTimeout: 5,
		ReadWriteTimeout: 5,
		MaxPoolSize:      100,
		MinPoolSize:      20,
		MaxWaitSize:      5,
		HealthSecond:     5,
		IdleTime:         5,
		Password:         c.cfg.Password,
		RetryEnabled:     true,
		ConnectTimeout:   5,
	})
	if err != nil {
		return err
	}

	if err := conn.Start(); err != nil {
		conn.Close()
		return nil
	}

	c.conn = conn
	return nil
}

func (c *Cache) Close() error {
	c.conn.Close()
	return nil
}

func init() {
	factory.Register("ssdb", NewCache)
}

func NewCache() factory.Cache {
	return &Cache{
		cfg: Options{
			Host: "127.0.0.1",
			Port: 8888,
		},
	}
}

func WithSSDBOptions(cfg Options) factory.CacheOption {
	return func(c factory.Cache) {
		if cache, ok := c.(*Cache); ok {
			cache.cfg = cfg
		}
	}
}
